home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / perl5 / RPC / XML / Procedure.pm < prev    next >
Encoding:
Perl POD Document  |  2008-04-10  |  34.2 KB  |  965 lines

  1. ###############################################################################
  2. #
  3. # This file copyright (c) 2001-2008 Randy J. Ray, all rights reserved
  4. #
  5. # See "LICENSE" in the documentation for licensing and redistribution terms.
  6. #
  7. ###############################################################################
  8. #
  9. #   $Id: Procedure.pm 343 2008-04-09 09:54:36Z rjray $
  10. #
  11. #   Description:    This class abstracts out all the procedure-related
  12. #                   operations from the RPC::XML::Server class
  13. #
  14. #   Functions:      new
  15. #                   name        \
  16. #                   code         \
  17. #                   signature     \ These are the accessor functions for the
  18. #                   help          / data in the object, though it's visible.
  19. #                   version      /
  20. #                   hidden      /
  21. #                   clone
  22. #                   is_valid
  23. #                   add_signature
  24. #                   delete_signature
  25. #                   make_sig_table
  26. #                   match_signature
  27. #                   reload
  28. #                   load_XPL_file
  29. #
  30. #   Libraries:      XML::Parser (used only on demand in load_XPL_file)
  31. #                   File::Spec
  32. #
  33. #   Global Consts:  $VERSION
  34. #
  35. #   Environment:    None.
  36. #
  37. ###############################################################################
  38.  
  39. package RPC::XML::Procedure;
  40.  
  41. use 5.005;
  42. use strict;
  43. use vars qw($VERSION);
  44. use subs qw(new is_valid name code signature help version hidden
  45.             add_signature delete_signature make_sig_table match_signature
  46.             reload load_XPL_file);
  47.  
  48. use AutoLoader 'AUTOLOAD';
  49. require File::Spec;
  50.  
  51. $VERSION = '1.15';
  52.  
  53. ###############################################################################
  54. #
  55. #   Sub Name:       new
  56. #
  57. #   Description:    Create a new object of this class, storing the info on
  58. #                   regular keys (no obfuscation used here).
  59. #
  60. #   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
  61. #                   $class    in      scalar    Class to bless into
  62. #                   @argz     in      variable  Disposition is variable; see
  63. #                                                 below
  64. #
  65. #   Returns:        Success:    object ref
  66. #                   Failure:    error string
  67. #
  68. ###############################################################################
  69. sub new
  70. {
  71.     my $class = shift;
  72.     my @argz  = @_;
  73.  
  74.     my $data; # This will be a hashref that eventually gets blessed
  75.  
  76.     $class = ref($class) || $class;
  77.  
  78.     #
  79.     # There are three things that @argz could be:
  80.     #
  81.     if (ref $argz[0])
  82.     {
  83.         # 1. A hashref containing all the relevant keys
  84.         $data = {};
  85.         %$data = %{$argz[0]};
  86.     }
  87.     elsif (@argz == 1)
  88.     {
  89.         # 2. Exactly one non-ref element, a file to load
  90.  
  91.         # And here is where I cheat in a way that makes even me uncomfortable.
  92.         #
  93.         # Loading code from an XPL file, it can actually be of a type other
  94.         # than how this constructor was called. So what we are going to do is
  95.         # this: If $class is undef, that can only mean that we were called
  96.         # with the intent of letting the XPL file dictate the resulting object.
  97.         # If $class is set, then we'll call load_XPL_file normally, as a
  98.         # method, to allow for subclasses to tweak things.
  99.         if (defined $class)
  100.         {
  101.             $data = $class->load_XPL_file($argz[0]);
  102.             return $data unless ref $data; # load_XPL_path signalled an error
  103.         }
  104.         else
  105.         {
  106.             # Spoofing the "class" argument to load_XPL_file makes me feel
  107.             # even dirtier...
  108.             $data = load_XPL_file(\$class, $argz[0]);
  109.             return $data unless ref $data; # load_XPL_path signalled an error
  110.             $class = "RPC::XML::$class";
  111.         }
  112.     }
  113.     else
  114.     {
  115.         # 3. If there is more than one arg, it's a sort-of-hash. That is, the
  116.         #    key 'signature' is allowed to repeat.
  117.         my ($key, $val);
  118.         $data = {};
  119.         $data->{signature} = [];
  120.         while (@argz)
  121.         {
  122.             ($key, $val) = splice(@argz, 0, 2);
  123.             if ($key eq 'signature')
  124.             {
  125.                 # Since there may be more than one signature, we allow it to
  126.                 # repeat. Of course, that's also why we can't just take @argz
  127.                 # directly as a hash. *shrug*
  128.                 push(@{$data->{signature}},
  129.                      ref($val) ? join(' ', @$val) : $val);
  130.             }
  131.             elsif (exists $data->{$key})
  132.             {
  133.                 return "${class}::new: Key '$key' may not be repeated";
  134.             }
  135.             else
  136.             {
  137.                 $data->{$key} = $val;
  138.             }
  139.         }
  140.     }
  141.  
  142.     return "${class}::new: Missing required data"
  143.         unless (exists $data->{signature} and
  144.                 (ref($data->{signature}) eq 'ARRAY') and
  145.                 scalar(@{$data->{signature}}) and
  146.                 $data->{name} and $data->{code});
  147.     bless $data, $class;
  148.     # This needs to happen post-bless in case of error (for error messages)
  149.     $data->make_sig_table;
  150. }
  151.  
  152. ###############################################################################
  153. #
  154. #   Sub Name:       make_sig_table
  155. #
  156. #   Description:    Create a hash table of the signatures that maps to the
  157. #                   corresponding return type for that particular invocation.
  158. #                   Makes looking up call patterns much easier.
  159. #
  160. #   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
  161. #                   $self     in      ref       Object of this class
  162. #
  163. #   Returns:        Success:    $self
  164. #                   Failure:    error message
  165. #
  166. ###############################################################################
  167. sub make_sig_table
  168. {
  169.     my $self = shift;
  170.  
  171.     my ($sig, $return, $rest);
  172.  
  173.     delete $self->{sig_table};
  174.     for $sig (@{$self->{signature}})
  175.     {
  176.         ($return, $rest) = split(/ /, $sig, 2); $rest = '' unless $rest;
  177.         # If the key $rest already exists, then this is a collision
  178.         return ref($self) . '::make_sig_table: Cannot have two different ' .
  179.             "return values for one set of params ($return vs. " .
  180.             "$self->{sig_table}->{$rest})"
  181.                 if $self->{sig_table}->{$rest};
  182.         $self->{sig_table}->{$rest} = $return;
  183.     }
  184.  
  185.     $self;
  186. }
  187.  
  188. #
  189. # These are basic accessor/setting functions for the various attributes
  190. #
  191. sub name      { $_[0]->{name}; } # "name" cannot be changed at this level
  192. sub help      { $_[1] and $_[0]->{help}    = $_[1]; $_[0]->{help};    }
  193. sub version   { $_[1] and $_[0]->{version} = $_[1]; $_[0]->{version}; }
  194. sub hidden    { $_[1] and $_[0]->{hidden}  = $_[1]; $_[0]->{hidden};  }
  195. sub code
  196. {
  197.     ref $_[1] eq 'CODE' and $_[0]->{code} = $_[1];
  198.     $_[0]->{code};
  199. }
  200. sub signature
  201. {
  202.     if ($_[1] and ref $_[1] eq 'ARRAY')
  203.     {
  204.         my $old = $_[0]->{signature};
  205.         $_[0]->{signature} = $_[1];
  206.         unless (ref($_[0]->make_sig_table))
  207.         {
  208.             # If it failed to re-init the table, restore the old list (and old
  209.             # table). We don't have to check this return, since it had worked
  210.             $_[0]->{signature} = $old;
  211.             $_[0]->make_sig_table;
  212.         }
  213.     }
  214.     # Return a copy of the array, not the original
  215.     [ @{$_[0]->{signature}} ];
  216. }
  217.  
  218. package RPC::XML::Method;
  219.  
  220. use strict;
  221.  
  222. @RPC::XML::Method::ISA = qw(RPC::XML::Procedure);
  223.  
  224. package RPC::XML::Procedure;
  225.  
  226. 1;
  227.  
  228. =head1 NAME
  229.  
  230. RPC::XML::Procedure - Object encapsulation of server-side RPC procedures
  231.  
  232. =head1 SYNOPSIS
  233.  
  234.     require RPC::XML::Procedure;
  235.  
  236.     ...
  237.     $method_1 = RPC::XML::Procedure->new({ name => 'system.identity',
  238.                                            code => sub { ... },
  239.                                            signature => [ 'string' ] });
  240.     $method_2 = RPC::XML::Procedure->new('/path/to/status.xpl');
  241.  
  242. =head1 IMPORTANT NOTE
  243.  
  244. This package is comprised of the code that was formerly B<RPC::XML::Method>.
  245. The package was renamed when the decision was made to support procedures and
  246. methods as functionally different entities. It is not necessary to include
  247. both this module and B<RPC::XML::Method> -- this module provides the latter as
  248. an empty subclass. In time, B<RPC::XML::Method> will be removed from the
  249. distribution entirely.
  250.  
  251. =head1 DESCRIPTION
  252.  
  253. The B<RPC::XML::Procedure> package is designed primarily for behind-the-scenes
  254. use by the B<RPC::XML::Server> class and any subclasses of it. It is
  255. documented here in case a project chooses to sub-class it for their purposes
  256. (which would require setting the C<method_class> attribute when creating
  257. server objects, see L<RPC::XML::Server>).
  258.  
  259. This package grew out of the increasing need to abstract the operations that
  260. related to the methods a given server instance was providing. Previously,
  261. methods were passed around simply as hash references. It was a small step then
  262. to move them into a package and allow for operations directly on the objects
  263. themselves. In the spirit of the original hashes, all the key data is kept in
  264. clear, intuitive hash keys (rather than obfuscated as the other classes
  265. do). Thus it is important to be clear on the interface here before
  266. sub-classing this package.
  267.  
  268. =head1 USAGE
  269.  
  270. The following methods are provided by this class:
  271.  
  272. =over 4
  273.  
  274. =item new(FILE|HASHREF|LIST)
  275.  
  276. Creates a new object of the class, and returns a reference to it. The
  277. arguments to the constructor are variable in nature, depending on the type:
  278.  
  279. =over 8
  280.  
  281. =item FILE
  282.  
  283. If there is exactly on argument that is not a reference, it is assumed to be a
  284. filename from which the method is to be loaded. This is presumed to be in the
  285. B<XPL> format descibed below (see L</"XPL File Structure">). If the file
  286. cannot be opened, or if once opened cannot be parsed, an error is raised.
  287.  
  288. =item HASHREF
  289.  
  290. If there is exactly one argument that is a reference, it is assumed to be a
  291. hash with the relevant information on the same keys as the object itself
  292. uses. This is primarily to support backwards-compatibility to code written
  293. when methods were implemented simply as hash references.
  294.  
  295. =item LIST
  296.  
  297. If there is more than one argument in the list, then the list is assumed to be
  298. a sort of "ersatz" hash construct, in that one of the keys (C<signature>) is
  299. allowed to occur multiple times. Otherwise, each of the following is allowed,
  300. but may only occur once:
  301.  
  302. =over 12
  303.  
  304. =item name
  305.  
  306. The name of the method, as it will be presented to clients
  307.  
  308. =item code
  309.  
  310. A reference to a subroutine, or an anonymous subroutine, that will receive
  311. calls for the method
  312.  
  313. =item signature
  314.  
  315. (May appear more than once) Provides one calling-signature for the method, as
  316. either a space-separated string of types or a list-reference
  317.  
  318. =item help
  319.  
  320. The help-text for a method, which is generally used as a part of the
  321. introspection interface for a server
  322.  
  323. =item version
  324.  
  325. The version number/string for the method
  326.  
  327. =item hidden
  328.  
  329. A boolean (true or false) value indicating whether the method should be hidden
  330. from introspection and similar listings
  331.  
  332. =back
  333.  
  334. Note that all of these correspond to the values that can be changed via the
  335. accessor methods detailed later.
  336.  
  337. =back
  338.  
  339. If any error occurs during object creation, an error message is returned in
  340. lieu of the object reference.
  341.  
  342. =item clone
  343.  
  344. Create a copy of the calling object, and return the new reference. All
  345. elements are copied over cleanly, except for the code reference stored on the
  346. C<code> hash key. The clone will point to the same code reference as the
  347. original. Elements such as C<signature> are copied, so that changes to the
  348. clone will not impact the original.
  349.  
  350. =item name
  351.  
  352. Returns the name by which the server is advertising the method. Unlike the
  353. next few accessors, this cannot be changed on an object. In order to
  354. streamline the managment of methods within the server classes, this must
  355. persist. However, the other elements may be used in the creation of a new
  356. object, which may then be added to the server, if the name absolutely must
  357. change.
  358.  
  359. =item code([NEW])
  360.  
  361. Returns or sets the code-reference that will receive calls as marshalled by
  362. the server. The existing value is lost, so if it must be preserved, then it
  363. should be retrieved prior to the new value being set.
  364.  
  365. =item signature([NEW])
  366.  
  367. Return a list reference containing the signatures, or set it. Each element of
  368. the list is a string of space-separated types (the first of which is the
  369. return type the method produces in that calling context). If this is being
  370. used to set the signature, then an array reference must be passed that
  371. contains one or more strings of this nature. Nested list references are not
  372. allowed at this level. If the new signatures would cause a conflict (a case in
  373. which the same set of input types are specified for different output types),
  374. the old set is silently restored.
  375.  
  376. =item help([NEW])
  377.  
  378. Returns or sets the help-text for the method. As with B<code>, the previous
  379. value is lost.
  380.  
  381. =item hidden([NEW])
  382.  
  383. Returns or sets the hidden status of the method. Setting it loses the previous
  384. value.
  385.  
  386. =item version([NEW])
  387.  
  388. Returns or sets the version string for the method (overwriting as with the
  389. other accessors).
  390.  
  391. =item is_valid
  392.  
  393. Returns a true/false value as to whether the object currently has enough
  394. content to be a valid method for a server to publish. This entails having at
  395. the very least a name, one or more signatures, and a code-reference to route
  396. the calls to. A server created from the classes in this software suite will
  397. not accept a method that is not valid.
  398.  
  399. =item add_signature(LIST)
  400.  
  401. Add one or more signatures (which may be a list reference or a string) to the
  402. internal tables for this method. Duplicate signatures are ignored. If the new
  403. signature would cause a conflict (a case in which the same set of input types
  404. are specified for different output types), the old set is restored and an
  405. error message is returned.
  406.  
  407. =item delete_signature(LIST)
  408.  
  409. Deletes the signature or signatures (list reference or string) from the
  410. internal tables. Quietly ignores any signature that does not exist. If the new
  411. signature would cause a conflict (a case in which the same set of input types
  412. are specified for different output types), the old set is restored and an
  413. error message is returned.
  414.  
  415. =item match_signature(SIGNATURE)
  416.  
  417. Check that the passed-in signature is known to the method, and if so returns
  418. the type that the method should be returning as a result of the call. Returns
  419. a zero (0) otherwise. This differs from other signature operations in that the
  420. passed-in signature (which may be a list-reference or a string) B<I<does not
  421. include the return type>>. This method is provided so that servers may check a
  422. list of arguments against type when marshalling an incoming call. For example,
  423. a signature of C<'int int'> would be tested for by calling
  424. C<$M-E<gt>match_signature('int')> and expecting the return value to be C<int>.
  425.  
  426. =item call(SERVER, PARAMLIST)
  427.  
  428. Execute the code that this object encapsulates, using the list of parameters
  429. passed in PARAMLIST. The SERVER argument should be an object derived from the
  430. B<RPC::XML::Server> class. For some types of procedure objects, this becomes
  431. the first argument of the parameter list to simulate a method call as if it
  432. were on the server object itself. The return value should be a data object
  433. (possibly a B<RPC::XML::fault>), but may not always be pre-encoded. Errors
  434. trapped in C<$@> are converted to fault objects. This method is generally used
  435. in the C<dispatch> method of the server class, where the return value is
  436. subsequently wrapped within a B<RPC::XML::response> object.
  437.  
  438. =item reload
  439.  
  440. Instruct the object to reload itself from the file it originally was loaded
  441. from, assuming that it was loaded from a file to begin with. Returns an error
  442. if the method was not originally loaded from a file, or if an error occurs
  443. during the reloading operation.
  444.  
  445. =back
  446.  
  447. =head2 Additional Hash Data
  448.  
  449. In addition to the attributes managed by the accessors documented earlier, the
  450. following hash keys are also available for use. These are also not strongly
  451. protected, and the same care should be taken before altering any of them:
  452.  
  453. =over 4
  454.  
  455. =item file
  456.  
  457. When the method was loaded from a file, this key contains the path to the file
  458. used.
  459.  
  460. =item mtime
  461.  
  462. When the method was loaded from a file, this key contains the
  463. modification-time of the file, as a UNIX-style C<time> value. This is used to
  464. check for changes to the file the code was originally read from.
  465.  
  466. =item called
  467.  
  468. When the method is being used by one of the server classes provided in this
  469. software suite, this key is incremented each time the server object dispatches
  470. a request to the method. This can later be checked to provide some indication
  471. of how frequently the method is being invoked.
  472.  
  473. =back
  474.  
  475. =head2 XPL File Structure
  476.  
  477. This section focuses on the way in which methods are expressed in these files,
  478. referred to here as "XPL files" due to the C<*.xpl> filename extension
  479. (which stands for "XML Procedure Layout"). This mini-dialect, based on XML,
  480. is meant to provide a simple means of specifying method definitions separate
  481. from the code that comprises the application itself. Thus, methods may
  482. theoretically be added, removed, debugged or even changed entirely without
  483. requiring that the server application itself be rebuilt (or, possibly, without
  484. it even being restarted).
  485.  
  486. =over 4
  487.  
  488. =item The XML-based file structure
  489.  
  490. The B<XPL Procedure Layout> dialect is a very simple application of XML to the
  491. problem of expressing the method in such a way that it could be useful to
  492. other packages than this one, or useful in other contexts than this one.
  493.  
  494. The lightweight DTD for the layout can be summarized as:
  495.  
  496.         <!ELEMENT  proceduredef  (name, version?, hidden?, signature+,
  497.                                   help?, code)>
  498.         <!ELEMENT  methoddef  (name, version?, hidden?, signature+,
  499.                                help?, code)>
  500.         <!ELEMENT  name       (#PCDATA)>
  501.         <!ELEMENT  version    (#PCDATA)>
  502.         <!ELEMENT  hidden     EMPTY>
  503.         <!ELEMENT  signature  (#PCDATA)>
  504.         <!ELEMENT  help       (#PCDATA)>
  505.         <!ELEMENT  code       (#PCDATA)>
  506.         <!ATTLIST  code       language (#PCDATA)>
  507.  
  508. The containing tag is always one of C<E<lt>methoddefE<gt>> or
  509. C<E<lt>proceduredefE<gt>>. The tags that specify name, signatures and the code
  510. itself must always be present. Some optional information may also be
  511. supplied. The "help" text, or what an introspection API would expect to use to
  512. document the method, is also marked as optional.  Having some degree of
  513. documentation for all the methods a server provides is a good rule of thumb,
  514. however.
  515.  
  516. The default methods that this package provides are turned into XPL files by
  517. the B<make_method> tool (see L<make_method>). The final forms of these may
  518. serve as direct examples of what the file should look like.
  519.  
  520. =item Information used only for book-keeping
  521.  
  522. Some of the information in the XPL file is only for book-keeping: the version
  523. stamp of a method is never involved in the invocation. The server also keeps
  524. track of the last-modified time of the file the method is read from, as well
  525. as the full directory path to that file. The C<E<lt>hidden /E<gt>> tag is used
  526. to identify those methods that should not be exposed to the outside world
  527. through any sort of introspection/documentation API. They are still available
  528. and callable, but the client must possess the interface information in order
  529. to do so.
  530.  
  531. =item The information crucial to the method
  532.  
  533. The name, signatures and code must be present for obvious reasons. The
  534. C<E<lt>nameE<gt>> tag tells the server what external name this procedure is
  535. known by. The C<E<lt>signatureE<gt>> tag, which may appear more than once,
  536. provides the definition of the interface to the function in terms of what
  537. types and quantity of arguments it will accept, and for a given set of
  538. arguments what the type of the returned value is. Lastly is the
  539. C<E<lt>codeE<gt>> tag, without which there is no procedure to remotely call.
  540.  
  541. =item Why the <code> tag allows multiple languages
  542.  
  543. Note that the C<E<lt>codeE<gt>> tag is the only one with an attribute, in this
  544. case "language". This is designed to allow for one XPL file to provide a given
  545. method in multiple languages. Why, one might ask, would there be a need for
  546. this?
  547.  
  548. It is the hope behind this package that collections of RPC suites may one day
  549. be made available as separate entities from this specific software package.
  550. Given this hope, it is not unreasonable to suggest that such a suite of code
  551. might be implemented in more than one language (each of Perl, Python, Ruby and
  552. Tcl, for example). Languages which all support the means by which to take new
  553. code and add it to a running process on demand (usually through an "C<eval>"
  554. keyword or something similar). If the file F<A.xpl> is provided with
  555. implementations in all four of the above languages, the name, help text,
  556. signature and even hidden status would likely be identical. So, why not share
  557. the non-language-specific elements in the spirit of re-use?
  558.  
  559. =item The "make_method" utility
  560.  
  561. The utility script C<make_method> is provided as a part of this software
  562. suite. It allows for the automatic creation of XPL files from either
  563. command-line information or from template files. It has a wide variety of
  564. features and options, and is out of the scope of this particular manual
  565. page. The package F<Makefile.PL> features an example of engineering the
  566. automatic generation of XPL files and their delivery as a part of the normal
  567. Perl module build process. Using this tool is highly recommended over managing
  568. XPL files directly. For the full details, see L<make_method>.
  569.  
  570. =back
  571.  
  572. =head1 DIAGNOSTICS
  573.  
  574. Unless otherwise noted in the individual documentation sections, all methods
  575. return the object reference on success, or a (non-reference) text string
  576. containing the error message upon failure.
  577.  
  578. =head1 CAVEATS
  579.  
  580. Moving the method management to a separate class adds a good deal of overhead
  581. to the general system. The trade-off in reduced complexity and added
  582. maintainability should offset this.
  583.  
  584. =head1 LICENSE
  585.  
  586. This module and the code within are released under the terms of the Artistic
  587. License 2.0
  588. (http://www.opensource.org/licenses/artistic-license-2.0.php). This code may
  589. be redistributed under either the Artistic License or the GNU Lesser General
  590. Public License (LGPL) version 2.1
  591. (http://www.opensource.org/licenses/lgpl-license.php).
  592.  
  593. =head1 SEE ALSO
  594.  
  595. L<RPC::XML::Server>, L<make_method>
  596.  
  597. =head1 AUTHOR
  598.  
  599. Randy J. Ray <rjray@blackperl.com>
  600.  
  601. =cut
  602.  
  603. __END__
  604.  
  605. ###############################################################################
  606. #
  607. #   Sub Name:       clone
  608. #
  609. #   Description:    Create a near-exact copy of the invoking object, save that
  610. #                   the listref in the "signature" key is a copy, not a ref
  611. #                   to the same list.
  612. #
  613. #   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
  614. #                   $self     in      ref       Object of this class
  615. #
  616. #   Returns:        Success:    $new_self
  617. #                   Failure:    error message
  618. #
  619. ###############################################################################
  620. sub clone
  621. {
  622.     my $self = shift;
  623.  
  624.     my $new_self = {};
  625.     for (keys %$self)
  626.     {
  627.         next if $_ eq 'signature';
  628.         $new_self->{$_} = $self->{$_};
  629.     }
  630.     $new_self->{signature} = [];
  631.     @{$new_self->{signature}} = @{$self->{signature}};
  632.  
  633.     bless $new_self, ref($self);
  634. }
  635.  
  636. ###############################################################################
  637. #
  638. #   Sub Name:       is_valid
  639. #
  640. #   Description:    Boolean test to tell if the calling object has sufficient
  641. #                   data to be used as a server method for RPC::XML::Server or
  642. #                   Apache::RPC::Server.
  643. #
  644. #   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
  645. #                   $self     in      ref       Object to test
  646. #
  647. #   Returns:        Success:    1, valid/complete
  648. #                   Failure:    0, invalid/incomplete
  649. #
  650. ###############################################################################
  651. sub is_valid
  652. {
  653.     my $self = shift;
  654.  
  655.     return ((ref($self->{code}) eq 'CODE') and $self->{name} and
  656.             (ref($self->{signature}) && scalar(@{$self->{signature}})));
  657. }
  658.  
  659. ###############################################################################
  660. #
  661. #   Sub Name:       add_signature
  662. #                   delete_signature
  663. #
  664. #   Description:    This pair of functions may be used to add and remove
  665. #                   signatures from a method-object.
  666. #
  667. #   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
  668. #                   $self     in      ref       Object of this class
  669. #                   @args     in      list      One or more signatures
  670. #
  671. #   Returns:        Success:    $self
  672. #                   Failure:    error string
  673. #
  674. ###############################################################################
  675. sub add_signature
  676. {
  677.     my $self = shift;
  678.     my @args = @_;
  679.  
  680.     my (%sigs, $one_sig, $tmp, $old);
  681.  
  682.     # Preserve the original in case adding the new one causes a problem
  683.     $old = $self->{signature};
  684.     %sigs = map { $_ => 1 } @{$self->{signature}};
  685.     for $one_sig (@args)
  686.     {
  687.         $tmp = (ref $one_sig) ? join(' ', @$one_sig) : $one_sig;
  688.         $sigs{$tmp} = 1;
  689.     }
  690.     $self->{signature} = [ keys %sigs ];
  691.     unless (ref($tmp = $self->make_sig_table))
  692.     {
  693.         # Because this failed, we have to restore the old table and return
  694.         # an error
  695.         $self->{signature} = $old;
  696.         $self->make_sig_table;
  697.         return ref($self) . '::add_signature: Error re-hashing table: ' .
  698.             $tmp;
  699.     }
  700.  
  701.     $self;
  702. }
  703.  
  704. sub delete_signature
  705. {
  706.     my $self = shift;
  707.     my @args = @_;
  708.  
  709.     my (%sigs, $one_sig, $tmp, $old);
  710.  
  711.     # Preserve the original in case adding the new one causes a problem
  712.     $old = $self->{signature};
  713.     %sigs = map { $_ => 1 } @{$self->{signature}};
  714.     for $one_sig (@args)
  715.     {
  716.         $tmp = (ref $one_sig) ? join(' ', @$one_sig) : $one_sig;
  717.         delete $sigs{$tmp};
  718.     }
  719.     $self->{signature} = [ keys %sigs ];
  720.     unless (ref($tmp = $self->make_sig_table))
  721.     {
  722.         # Because this failed, we have to restore the old table and return
  723.         # an error
  724.         $self->{signature} = $old;
  725.         $self->make_sig_table;
  726.         return ref($self) . '::delete_signature: Error re-hashing table: ' .
  727.             $tmp;
  728.     }
  729.  
  730.     $self;
  731. }
  732.  
  733. ###############################################################################
  734. #
  735. #   Sub Name:       match_signature
  736. #
  737. #   Description:    Determine if the passed-in signature string matches any
  738. #                   of this method's known signatures.
  739. #
  740. #   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
  741. #                   $self     in      ref       Object of this class
  742. #                   $sig      in      scalar    Signature to check for
  743. #
  744. #   Returns:        Success:    return type as a string
  745. #                   Failure:    0
  746. #
  747. ###############################################################################
  748. sub match_signature
  749. {
  750.     my $self = shift;
  751.     my $sig  = shift;
  752.  
  753.     $sig = join(' ', @$sig) if ref $sig;
  754.  
  755.     return $self->{sig_table}->{$sig} || 0;
  756. }
  757.  
  758. ###############################################################################
  759. #
  760. #   Sub Name:       reload
  761. #
  762. #   Description:    Reload the method's code and ancillary data from the file
  763. #
  764. #   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
  765. #                   $self     in      ref       Object of this class
  766. #
  767. #   Returns:        Success:    $self
  768. #                   Failure:    error message
  769. #
  770. ###############################################################################
  771. sub reload
  772. {
  773.     my $self = shift;
  774.  
  775.     return ref($self) . '::reload: No file associated with method ' .
  776.         $self->{name} unless $self->{file};
  777.     my $tmp = $self->load_XPL_file($self->{file});
  778.  
  779.     if (ref $tmp)
  780.     {
  781.         # Update the information on this actual object
  782.         $self->{$_} = $tmp->{$_} for (keys %$tmp);
  783.         # Re-calculate the signature table, in case that changed as well
  784.         return $self->make_sig_table;
  785.     }
  786.  
  787.     return $tmp;
  788. }
  789.  
  790. ###############################################################################
  791. #
  792. #   Sub Name:       load_XPL_file
  793. #
  794. #   Description:    Load a XML-encoded method description (generally denoted
  795. #                   by a *.xpl suffix) and return the relevant information.
  796. #
  797. #                   Note that this does not fill in $self if $self is a hash
  798. #                   or object reference. This routine is not a substitute for
  799. #                   calling new() (which is why it isn't part of the public
  800. #                   API).
  801. #
  802. #   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
  803. #                   $self     in      ref       Object of this class
  804. #                   $file     in      scalar    File to load
  805. #
  806. #   Returns:        Success:    hashref of values
  807. #                   Failure:    error string
  808. #
  809. ###############################################################################
  810. sub load_XPL_file
  811. {
  812.     my $self = shift;
  813.     my $file = shift;
  814.  
  815.     require XML::Parser;
  816.  
  817.     my ($me, $pkg, $data, $signature, $code, $codetext, $accum, $P, %attr);
  818.     local *F;
  819.  
  820.     if (ref($self) eq 'SCALAR')
  821.     {
  822.         $me = __PACKAGE__ . '::load_XPL_file';
  823.     }
  824.     else
  825.     {
  826.         $me = (ref $self) || $self || __PACKAGE__;
  827.         $me .= '::load_XPL_file';
  828.     }
  829.     $data = {};
  830.     # So these don't end up undef, since they're optional elements
  831.     $data->{hidden} = 0; $data->{version} = ''; $data->{help} = '';
  832.     $data->{called} = 0;
  833.     open(F, "< $file") or return "$me: Error opening $file for reading: $!";
  834.     $P = XML::Parser
  835.         ->new(Handlers => {Char  => sub { $accum .= $_[1] },
  836.                            Start => sub { %attr = splice(@_, 2) },
  837.                            End   =>
  838.                            sub {
  839.                                my $elem = $_[1];
  840.  
  841.                                $accum =~ s/^[\s\n]+//;
  842.                                $accum =~ s/[\s\n]+$//;
  843.                                if ($elem eq 'signature')
  844.                                {
  845.                                    $data->{signature} ||= [];
  846.                                    push(@{$data->{signature}}, $accum);
  847.                                }
  848.                                elsif ($elem eq 'code')
  849.                                {
  850.                                    $data->{$elem} = $accum
  851.                                        unless ($attr{language} and
  852.                                                $attr{language} ne 'perl');
  853.                                }
  854.                                elsif (substr($elem, -3) eq 'def')
  855.                                {
  856.                                    # Don't blindly store the container tag...
  857.                                    # We may need it to tell the caller what
  858.                                    # our type is
  859.                                    $$self = ucfirst substr($elem, 0, -3)
  860.                                        if (ref($self) eq 'SCALAR');
  861.                                }
  862.                                else
  863.                                {
  864.                                    $data->{$elem} = $accum;
  865.                                }
  866.  
  867.                                %attr = ();
  868.                                $accum = '';
  869.                            }});
  870.     return "$me: Error creating XML::Parser object" unless $P;
  871.     # Trap any errors
  872.     eval { $P->parse(*F) };
  873.     close(F);
  874.     return "$me: Error parsing $file: $@" if $@;
  875.  
  876.     # Try to normalize $codetext before passing it to eval
  877.     my $class = __PACKAGE__; # token won't expand in the s/// below
  878.     ($codetext = $data->{code}) =~
  879.         s/sub[\s\n]+([\w:]+)?[\s\n]*\{/sub \{ package $class; /;
  880.     $code = eval $codetext;
  881.     return "$me: Error creating anonymous sub: $@" if $@;
  882.  
  883.     $data->{code} = $code;
  884.     # Add the file's mtime for when we check for stat-based reloading
  885.     $data->{mtime} = (stat $file)[9];
  886.     $data->{file} = $file;
  887.  
  888.     $data;
  889. }
  890.  
  891. ###############################################################################
  892. #
  893. #   Sub Name:       call
  894. #
  895. #   Description:    Encapsulates the invocation of the code block that the
  896. #                   object is abstracting. Manages parameters, signature
  897. #                   checking, etc.
  898. #
  899. #   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
  900. #                   $self     in      ref       Object of this class
  901. #                   $srv      in      ref       An object derived from the
  902. #                                                 RPC::XML::Server class
  903. #                   @dafa     in      list      The params for the call itself
  904. #
  905. #   Globals:        None.
  906. #
  907. #   Environment:    None.
  908. #
  909. #   Returns:        Success:    value
  910. #                   Failure:    dies with RPC::XML::Fault object as message
  911. #
  912. ###############################################################################
  913. sub call
  914. {
  915.     my ($self, $srv, @data) = @_;
  916.  
  917.     my (@paramtypes, @params, $signature, $resptype, $response, $name, $noinc);
  918.  
  919.     $name = $self->name;
  920.     # Create the param list.
  921.     # The type for the response will be derived from the matching signature
  922.     @paramtypes = map { $_->type  } @data;
  923.     @params     = map { $_->value } @data;
  924.     $signature = join(' ', @paramtypes);
  925.     $resptype = $self->match_signature($signature);
  926.     # Since there must be at least one signature with a return value (even
  927.     # if the param list is empty), this tells us if the signature matches:
  928.     return RPC::XML::fault->new(301,
  929.                                 "method $name has no matching " .
  930.                                 'signature for the argument list: ' .
  931.                                 "[$signature]")
  932.         unless ($resptype);
  933.  
  934.     # Set these in case the server object is part of the param list
  935.     local $srv->{signature} = [ $resptype, @paramtypes ];
  936.     local $srv->{method_name} = $name;
  937.     # If the method being called is "system.status", check to see if we should
  938.     # increment the server call-count.
  939.     $noinc = (($name eq 'system.status') && @data &&
  940.               ($paramtypes[0] eq 'boolean') && $params[0]) ? 1 : 0;
  941.     # For RPC::XML::Method (and derivatives), pass the server object
  942.     unshift(@params, $srv) if ($self->isa('RPC::XML::Method'));
  943.  
  944.     # Now take a deep breath and call the method with the arguments
  945.     eval { $response = $self->{code}->(@params); };
  946.     # On failure, propagate user-generated RPC::XML::fault exceptions, or
  947.     # transform Perl-level error/failure into such an object
  948.     if ($@)
  949.     {
  950.         return UNIVERSAL::isa($@, 'RPC::XML::fault') ?
  951.             $@ :
  952.             RPC::XML::fault->new(302, "Method $name returned error: $@");
  953.     }
  954.  
  955.     $self->{called}++ unless $noinc;
  956.     # Create a suitable return value
  957.     if ((! ref($response)) && UNIVERSAL::can("RPC::XML::$resptype", 'new'))
  958.     {
  959.         my $class = "RPC::XML::$resptype";
  960.         $response = $class->new($response);
  961.     }
  962.  
  963.     $response;
  964. }
  965.